#include "ex14_24.h"
#include <algorithm>

// constructor taking Size as days
// the argument must be within (0, 2^32)
Date::Date(Size days) {
  // calculate the year
  Size y400 = days / YtoD_400;
  Size y100 = (days - y400 * YtoD_400) / YtoD_100;
  Size y4 = (days - y400 * YtoD_400 - y100 * YtoD_100) / YtoD_4;
  Size y = (days - y400 * YtoD_400 - y100 * YtoD_100 - y4 * YtoD_4) / 365;
  Size d = days - y400 * YtoD_400 - y100 * YtoD_100 - y4 * YtoD_4 - y * 365;
  this->year = y400 * 400 + y100 * 100 + y4 * 4 + y;

  // check if leap and choose the months vector accordingly
  std::vector<Size> currYear =
      isLeapYear(this->year) ? monthsVec_l : monthsVec_n;

  // calculate day and month using find_if + lambda
  Size D_accumu = 0, M_accumu = 0;
  // @bug    fixed:  the variabbles above hade been declared inside the find_if
  // as static
  //                 which caused the bug. It works fine now after being move
  //                 outside.

  std::find_if(currYear.cbegin(), currYear.cend(), [&](Size m) {
    D_accumu += m;
    M_accumu++;

    if (d < D_accumu) {
      this->month = M_accumu;
      this->day = d + m - D_accumu;

      return true;
    } else
      return false;
  });
}

// construcotr taking iostream
Date::Date(std::istream &is, std::ostream &os) {
  is >> day >> month >> year;

  if (is) {
    if (check(*this))
      return;
    else {
      os << "Invalid input! Object is default initialized.";
      *this = Date();
    }
  } else {
    os << "Invalid input! Object is default initialized.";
    *this = Date();
  }
}

// copy constructor
Date::Date(const Date &d) : day(d.day), month(d.month), year(d.year) {}

// move constructor
Date::Date(Date &&d) NOEXCEPT : day(d.day), month(d.month), year(d.year) {
  std::cout << "copy moving";
}

// copy operator=
Date &Date::operator=(const Date &d) {
  this->day = d.day;
  this->month = d.month;
  this->year = d.year;

  return *this;
}

// move operator=
Date &Date::operator=(Date &&rhs) NOEXCEPT {
  if (this != &rhs) {
    this->day = rhs.day;
    this->month = rhs.month;
    this->year = rhs.year;
  }
  std::cout << "moving =";

  return *this;
}

// conver to days
Date::Size Date::toDays() const {
  Size result = this->day;

  // check if leap and choose the months vector accordingly
  std::vector<Size> currYear =
      isLeapYear(this->year) ? monthsVec_l : monthsVec_n;

  // calculate result + days by months
  for (auto it = currYear.cbegin(); it != currYear.cbegin() + this->month - 1;
       ++it)
    result += *it;

  // calculate result + days by years
  result += (this->year / 400) * YtoD_400;
  result += (this->year % 400 / 100) * YtoD_100;
  result += (this->year % 100 / 4) * YtoD_4;
  result += (this->year % 4) * YtoD_1;

  return result;
}

// member operators:   +=  -=

Date &Date::operator+=(Date::Size offset) {
  *this = Date(this->toDays() + offset);
  return *this;
}

Date &Date::operator-=(Date::Size offset) {
  if (this->toDays() > offset)
    *this = Date(this->toDays() - offset);
  else
    *this = Date();

  return *this;
}

// non-member operators:  <<  >>  -   ==  !=  <   <=  >   >=

std::ostream &operator<<(std::ostream &os, const Date &d) {
  os << d.day << " " << d.month << " " << d.year;
  return os;
}

std::istream &operator>>(std::istream &is, Date &d) {
  if (is) {
    Date input = Date(is, std::cout);
    if (check(input))
      d = input;
  }
  return is;
}

int operator-(const Date &lhs, const Date &rhs) {
  return lhs.toDays() - rhs.toDays();
}

bool operator==(const Date &lhs, const Date &rhs) {
  return (lhs.day == rhs.day) && (lhs.month == rhs.month) &&
         (lhs.year == rhs.year);
}

bool operator!=(const Date &lhs, const Date &rhs) { return !(lhs == rhs); }

bool operator<(const Date &lhs, const Date &rhs) {
  return lhs.toDays() < rhs.toDays();
}

bool operator<=(const Date &lhs, const Date &rhs) {
  return (lhs < rhs) || (lhs == rhs);
}

bool operator>(const Date &lhs, const Date &rhs) { return !(lhs <= rhs); }

bool operator>=(const Date &lhs, const Date &rhs) { return !(lhs < rhs); }

Date operator-(const Date &lhs,
               Date::Size rhs) { //  ^^^ rhs must not be larger than 2^32-1
  // copy lhs
  Date result(lhs);
  result -= rhs;

  return result;
}

Date operator+(const Date &lhs,
               Date::Size rhs) { //  ^^^ rhs must not be larger than 2^32-1
  // copy lhs
  Date result(lhs);
  result += rhs;

  return result;
}

int main() {

  Date lhs(9999999), rhs(1);

  std::cout << (lhs -= 12000) << "\n";

  return 0;
}
